//===--- Scope.h - Declarations for scope RAII objects ----------*- C++ -*-===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file defines the Scope and FullExpr RAII objects.
//
//===----------------------------------------------------------------------===//

#ifndef POLARPHP_PIL_GEN_SCOPE_H
#define POLARPHP_PIL_GEN_SCOPE_H

#include "polarphp/pil/gen/PILGenFunction.h"
#include "polarphp/pil/lang/PILDebugScope.h"
#include "polarphp/pil/gen/Cleanup.h"

namespace polar::lowering {

/// A Scope is a RAII object recording that a scope (e.g. a brace
/// statement) has been entered.
class LLVM_LIBRARY_VISIBILITY Scope{
   CleanupManager & cleanups;
   CleanupsDepth depth;
   Scope *savedInnermostScope;
   CleanupLocation loc;

   friend class CleanupManager;

public:
   explicit Scope(CleanupManager &cleanups, CleanupLocation loc)
      : cleanups(cleanups), depth(cleanups.getCleanupsDepth()),
        savedInnermostScope(cleanups.innermostScope), loc(loc) {
      assert(depth.isValid());
      cleanups.innermostScope = this;
      if (savedInnermostScope)
         cleanups.stack.checkIterator(savedInnermostScope->depth);
   }

   Scope(const Scope &other) = delete;
   Scope &operator=(const Scope &other) = delete;

   Scope(Scope &&other) = delete;
   Scope &operator=(Scope &&other) = delete; // implementable if needed

   explicit Scope(PILGenFunction &SGF, PILLocation loc)
      : Scope(SGF.Cleanups, CleanupLocation::get(loc)) {}

   void pop() {
      assert(depth.isValid() && "popping a scope twice!");
      popImpl();
      depth = CleanupsDepth::invalid();
   }

   ~Scope() {
      if (depth.isValid())
         popImpl();
   }

   /// Verify that the invariants of this scope still hold.
   void verify();

   bool isValid() const { return depth.isValid(); }

   /// Pop the scope pushing the +1 ManagedValue through the scope. Asserts if mv
   /// is a plus zero managed value.
   ManagedValue popPreservingValue(ManagedValue mv);

   /// Pop this scope pushing the +1 rvalue through the scope. Asserts if rv is a
   /// plus zero rvalue.
   RValue popPreservingValue(RValue &&rv);

private:
   /// Internal private implementation of popImpl so we can use it in Scope::pop
   /// and in Scope's destructor.
   void popImpl();
};

/// A FullExpr is a RAII object recording that a full-expression has
/// been entered.  A full-expression is essentially a very small scope
/// for the temporaries in an expression, with the added complexity
/// that (eventually, very likely) we have to deal with expressions
/// that are only conditionally evaluated.
class LLVM_LIBRARY_VISIBILITY FullExpr

   : private Scope {
public:
   explicit FullExpr(CleanupManager
                     &cleanups,
                     CleanupLocation loc
   )
      :
      Scope(cleanups, loc
      ) {
   }
   using Scope::pop;
};

/// A LexicalScope is a Scope that is also exposed to the debug info.
class LLVM_LIBRARY_VISIBILITY LexicalScope

   : private Scope {
   PILGenFunction &SGF;
public:
   explicit LexicalScope(PILGenFunction
                         &SGF,
                         CleanupLocation loc
   )
      :
      Scope(SGF
               .Cleanups, loc),
      SGF(SGF) {
      SGF.enterDebugScope(loc);
   }
   using Scope::pop;

   ~LexicalScope() {
      SGF.leaveDebugScope();
   }

};

/// A scope that only exists in the debug info.
class LLVM_LIBRARY_VISIBILITY DebugScope{
   PILGenFunction & SGF;

public:
   explicit DebugScope(PILGenFunction &SGF, CleanupLocation loc) : SGF(SGF) {
      SGF.enterDebugScope(loc);
   }

   ~DebugScope() { SGF.leaveDebugScope(); }
};

} // polar::lowering

#endif // POLARPHP_PIL_GEN_SCOPE_H
